package com.fanhang.samples.spring.jdbc.common;

import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.sql.ResultSet;
import java.sql.ResultSetMetaData;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import javax.annotation.Resource;
import javax.sql.DataSource;

import org.springframework.dao.DataAccessException;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.ResultSetExtractor;
import org.springframework.jdbc.core.namedparam.EmptySqlParameterSource;
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource;
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcDaoSupport;
import org.springframework.jdbc.core.namedparam.SqlParameterSource;
import org.springframework.util.Assert;

/**
 * 数据库操作的封装
 */
public class BaseDaoImpl<T extends Serializable> extends NamedParameterJdbcDaoSupport {
	
	private Class<T> entityClass;
	
	@Resource(name = "dataSource")
	private void init(DataSource dataSource) {
		super.setDataSource(dataSource);
	}
	
	protected final ResultSetExtractor<List<String[]>> RSE_STRING_ARRAY = new ResultSetExtractor<List<String[]>>(){
		@Override
		public List<String[]> extractData(ResultSet rs) throws DataAccessException, DataAccessException, SQLException {
			ResultSetMetaData rsmd = rs.getMetaData();
			int columnCount = rsmd.getColumnCount();
			
			List<String[]> result = new ArrayList<String[]>();
			String[] row = null;
			while (rs.next()) {
				row = new String[columnCount];
				for (int i = 0; i < columnCount; i++) {
					row[i] = rs.getString(i+1);
				}
				result.add(row);
			}
			return result;
		}
	};
	
	@SuppressWarnings("unchecked")
	public BaseDaoImpl() {
		Type t = getClass().getGenericSuperclass();
		System.out.println(t);
		Type[] types = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments();
		if (types != null && types.length > 0) {
			entityClass = (Class<T>)types[0];
		}
	}
	
	protected int update(CharSequence sql, Map<String, ?> paramMap) {
		Assert.notNull(sql, "[sql] must not be null");
		Assert.notNull(paramMap, "[paramMap] must not be null");
		return this.update(sql, new MapSqlParameterSource(paramMap));
	}
	
	public int update(CharSequence sql, SqlParameterSource paramSource) throws DataAccessException {
		Assert.notNull(sql, "[sql] must not be null");
		Assert.notNull(paramSource, "[args] must not be null");
		return super.getNamedParameterJdbcTemplate().update(sql.toString(), paramSource);
	}
	
	
	protected int update(CharSequence sql) {
		Assert.notNull(sql, "[sql] must not be null");
		return super.getJdbcTemplate().update(sql.toString());
	}
	
	protected int update(String sql, Object[] args) throws DataAccessException {
		Assert.notNull(sql, "[sql] must not be null");
		Assert.notNull(args, "[args] must not be null");
		return super.getJdbcTemplate().update(sql.toString(), args);
	}
	
	protected Integer getCount(String sql) throws DataAccessException {
		return this.getCount(sql, EmptySqlParameterSource.INSTANCE);
	}
	
	protected int getCount(String sql, Map<String, ?> paramMap) throws DataAccessException {
		return this.getCount(sql, new MapSqlParameterSource(paramMap));
	}
	
	protected Integer getCount(String sql, SqlParameterSource paramSource) throws DataAccessException {
		return super.getNamedParameterJdbcTemplate().queryForObject(sql, paramSource, Integer.class);
	}
	
	protected T get(String sql) throws DataAccessException {
		return this.get(sql, new Object[]{});
	}
	
	protected T get(String sql, Object[] params) throws DataAccessException {
		return this.get(sql, params, entityClass);
	}
	
	protected T get(String sql, Object[] params, Class<T> clazz) throws DataAccessException {
		Assert.hasLength(sql);
		Assert.notNull(clazz);
		return super.getJdbcTemplate().queryForObject(sql, params, new BeanPropertyRowMapper<T>(clazz));
	}
	
	protected T get(String sql, Map<String, ?> paramMap) throws DataAccessException {
		SqlParameterSource paramSource = new MapSqlParameterSource(paramMap);
		return super.getNamedParameterJdbcTemplate().queryForObject(sql, paramSource, entityClass);
	}
	
	protected T get(String sql, SqlParameterSource paramSource) throws DataAccessException {
		return super.getNamedParameterJdbcTemplate().queryForObject(sql, paramSource, entityClass);
	}
	
	protected T get(String sql, SqlParameterSource paramSource, Class<T> clazz) throws DataAccessException {
		Assert.hasLength(sql);
		Assert.notNull(clazz);
		return super.getNamedParameterJdbcTemplate().queryForObject(sql, paramSource, clazz);
	}
	
	protected List<String[]> getList(String sql) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, RSE_STRING_ARRAY);
	}
	
	protected List<String[]> getList(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, params, RSE_STRING_ARRAY);
	}
	
	protected List<T> getList(String sql, Class<T> clazz) throws DataAccessException {
		return this.getList(sql, EmptySqlParameterSource.INSTANCE, entityClass);
	}
	
	protected List<T> getList(String sql, SqlParameterSource paramSource) throws DataAccessException {
		return this.getList(sql, paramSource, entityClass);
	}
	
	protected List<T> getList(String sql, SqlParameterSource paramSource, Class<T> clazz) throws DataAccessException {
		return super.getNamedParameterJdbcTemplate().query(sql, paramSource, new BeanPropertyRowMapper<T>(clazz));
	}
	
	protected List<String[]> getPageList(String sql, final int start, final int end) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, new ResultSetExtractor<List<String[]>>(){
			@Override
			public List<String[]> extractData(ResultSet rs) throws DataAccessException, DataAccessException, SQLException {
				ResultSetMetaData rsmd = rs.getMetaData();
				int columnCount = rsmd.getColumnCount();
				rs.absolute(start);
				int size = 0;
				List<String[]> result = new ArrayList<String[]>();
				String[] row = null;
				while (rs.next()) {
					if (size >= end) {
						break;
					}
					row = new String[columnCount];
					for (int i = 0; i < columnCount; i++) {
						row[i] = rs.getString(i+1);
					}
					result.add(row);
					size++;
				}
//				pagination.setRowSize(rs.getRow());
				return result;
			}
		});
	}
	
	protected boolean hasData(String sql) throws DataAccessException {
		return this.hasData(sql, EmptySqlParameterSource.INSTANCE);
	}
	
	protected boolean hasData(String sql, Object[] params) throws DataAccessException {
		return super.getJdbcTemplate().query(sql, params, new ResultSetExtractor<Boolean>(){
			@Override
			public Boolean extractData(ResultSet rs) throws DataAccessException, SQLException {
				return rs.next();
			}
		});
	}
	
	protected boolean hasData(String sql, SqlParameterSource paramSource) throws DataAccessException {
		return super.getNamedParameterJdbcTemplate().query(sql, paramSource, new ResultSetExtractor<Boolean>(){
			@Override
			public Boolean extractData(ResultSet rs) throws DataAccessException, SQLException {
				return rs.next();
			}
		});
	}
}
